package com.xuecheng.manage_cms.service;

import com.alibaba.fastjson.JSON;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.xuecheng.framework.domain.cms.CmsConfig;
import com.xuecheng.framework.domain.cms.CmsPage;
import com.xuecheng.framework.domain.cms.CmsTemplate;
import com.xuecheng.framework.domain.cms.request.QueryPageRequest;
import com.xuecheng.framework.domain.cms.response.CmsCode;
import com.xuecheng.framework.domain.cms.response.CmsPageResult;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.QueryResponseResult;
import com.xuecheng.framework.model.response.QueryResult;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.manage_cms.config.RabbitmqConfig;
import com.xuecheng.manage_cms.dao.CmsConfigRepository;
import com.xuecheng.manage_cms.dao.CmsPageRepository;
import com.xuecheng.manage_cms.dao.CmsTemplateRepository;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Service
public class PageService {

    @Autowired
    private CmsPageRepository cmsPageRepository;

    @Autowired
    private CmsConfigRepository cmsConfigRepository;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private CmsTemplateRepository cmsTemplateRepository;

    @Autowired
    private GridFsTemplate gridFsTemplate;

    @Autowired
    private GridFSBucket gridFSBucket;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 条件分页查询
     * @param page
     * @param size
     * @param queryPageRequest
     * @return
     */
    public QueryResponseResult findList(int page, int size, QueryPageRequest queryPageRequest){
        if(queryPageRequest == null){
            return null;
        }
        if(page <= 0){
            page = 1;
        }
        page = page - 1;
        if(size <= 0){
            size = 10;
        }

        // 构建查询条件
        ExampleMatcher exampleMatcher = ExampleMatcher.matching()
                .withMatcher("pageAliase", ExampleMatcher.GenericPropertyMatchers.contains());

        CmsPage cmsPage = new CmsPage();
        if(StringUtils.isNotEmpty(queryPageRequest.getSiteId())){
            cmsPage.setSiteId(queryPageRequest.getSiteId());
        }
        if(StringUtils.isNotEmpty(queryPageRequest.getPageAliase())){
            cmsPage.setPageAliase(queryPageRequest.getPageAliase());
        }
        Example<CmsPage> example = Example.of(cmsPage, exampleMatcher);
        Pageable pageable = PageRequest.of(page, size);
        Page<CmsPage> all = cmsPageRepository.findAll(example, pageable);

        //封装响应
        QueryResult<CmsPage> queryResult =  new QueryResult<>();
        queryResult.setList(all.getContent());//页数据
        queryResult.setTotal(all.getTotalElements());//总条数
        QueryResponseResult result = new QueryResponseResult(CommonCode.SUCCESS, queryResult);

        return result;
    }

    /**
     * 添加页面
     * @param cmsPage
     * @return
     */
    public CmsPageResult add(CmsPage cmsPage){
        CmsPage one = cmsPageRepository.findBySiteIdAndPageNameAndPageWebPath(cmsPage.getSiteId(), cmsPage.getPageName(), cmsPage.getPageWebPath());
        if(one != null){
            ExceptionCast.cast(CmsCode.CMS_ADDPAGE_EXISTSNAME);//页面已存在
        }
        cmsPage.setPageId(null);//让数据库生成主键
        CmsPage result = cmsPageRepository.save(cmsPage);
        return new CmsPageResult(CommonCode.SUCCESS, result);
    }

    public ResponseResult post(String pageId){
        //执行静态化
        String pageHtml = this.getPageHtml(pageId);

        //保存静态化文件
        CmsPage cmsPage = saveHtml(pageId, pageHtml);

        //发送消息
        sendPostPage(pageId);
        return new ResponseResult(CommonCode.SUCCESS);
    }

    private CmsPage saveHtml(String pageId, String pageHtml){
        //获取页面
        Optional<CmsPage> cmsPageOptional = cmsPageRepository.findById(pageId);
        if(!cmsPageOptional.isPresent()){
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISITS);
        }
        CmsPage cmsPage = cmsPageOptional.get();

        String htmlFileId = cmsPage.getHtmlFileId();

        //保存网页
        InputStream inputStream = null;
        try {
            inputStream = IOUtils.toInputStream(pageHtml, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        ObjectId objectId = gridFsTemplate.store(inputStream, cmsPage.getPageName());


        String fileId = objectId.toHexString();
        cmsPage.setHtmlFileId(fileId);
        cmsPageRepository.save(cmsPage);

        //删除旧的文件
        if(StringUtils.isNotEmpty(htmlFileId)){
            gridFsTemplate.delete(Query.query(Criteria.where("_id").is(htmlFileId)));
        }
        return cmsPage;
    }

    private void sendPostPage(String pageId){
        Optional<CmsPage> cmsPageOptional = cmsPageRepository.findById(pageId);
        if(!cmsPageOptional.isPresent()){
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISITS);
        }
        CmsPage cmsPage = cmsPageOptional.get();

        Map<String, String> msgMap = new HashMap();
        msgMap.put("pageId", pageId);
        String msg = JSON.toJSONString(msgMap);

        String siteId = cmsPage.getSiteId();

        rabbitTemplate.convertAndSend(RabbitmqConfig.EX_ROUTING_CMS_POSTPAGE, siteId, msg);
    }


    /**
     * 主键查询
     * @param pageId
     * @return
     */
    public CmsPage findById(String pageId){
        Optional<CmsPage> optional = cmsPageRepository.findById(pageId);
        return optional.isPresent() ? optional.get() : null;
    }

    /**
     * 更新页面
     * @param pageId
     * @param cmsPage
     * @return
     */
    public CmsPageResult updateById(String pageId, CmsPage cmsPage){
        //检查是否存在
        CmsPage one = findById(pageId);
        if(one != null){
            //更新站点id
            one.setSiteId(cmsPage.getSiteId());
            //更新模板id
            one.setTemplateId(cmsPage.getTemplateId());
            //更新页面名称
            one.setPageName(cmsPage.getPageName());
            //更新页面别名
            one.setPageAliase(cmsPage.getPageAliase());
            //更新访问路径
            one.setPageWebPath(cmsPage.getPageWebPath());
            //更新物理路径
            one.setPagePhysicalPath(cmsPage.getPagePhysicalPath());
            //更新 dataUrl
            one.setDataUrl(cmsPage.getDataUrl());
            CmsPage result = cmsPageRepository.save(one);
            if(result != null){
                return new CmsPageResult(CommonCode.SUCCESS, result);
            }
        }
        return new CmsPageResult(CommonCode.FAIL, null);
    }

    /**
     * 根据主键删除
     * @param pageId
     * @return
     */
    public ResponseResult delete(String pageId){
        CmsPage cmsPage = this.findById(pageId);
        if(cmsPage != null){
            cmsPageRepository.deleteById(pageId);
            return new ResponseResult(CommonCode.SUCCESS);
        }
        return new ResponseResult(CommonCode.FAIL);
    }

    public CmsConfig findConfigById(String id){
        return cmsConfigRepository.findById(id).orElse(null);
    }

    public String getPageHtml(String pageId){
        //获取数据模型
        Map model = getModelByPageId(pageId);
        if(model == null){//获取失败
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);
        }
        //获取页面模板
        String template = getTemplateByPageId(pageId);
        if(template == null){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }
        //执行静态化
        String html = generateHtml(template, model);
        return html;
    }

    //获取数据模型
    private Map getModelByPageId(String pageId){
        //获取页面模型
        CmsPage cmsPage = findById(pageId);
        if(cmsPage == null){
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISITS);
        }
        //获取dataUrl
        String dataUrl = cmsPage.getDataUrl();
        if(StringUtils.isEmpty(dataUrl)){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAURLISNULL);
        }
        ResponseEntity<Map> entity = restTemplate.getForEntity(dataUrl, Map.class);
        Map body = entity.getBody();
        return body;
    }

    private String getTemplateByPageId(String pageId){
        //获取页面模型
        CmsPage cmsPage = findById(pageId);
        if(cmsPage == null){
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISITS);
        }
        //获取模板对象
        String templateId = cmsPage.getTemplateId();
        if(StringUtils.isEmpty(templateId)){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }

        Optional<CmsTemplate> optionalCmsTemplate = cmsTemplateRepository.findById(templateId);
        CmsTemplate cmsTemplate = optionalCmsTemplate.orElse(new CmsTemplate());
        String templateFileId = cmsTemplate.getTemplateFileId();

        //主键查询
        GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId)));
        GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
        GridFsResource gridFsResource = new GridFsResource(gridFSFile, gridFSDownloadStream);
        String content = null;
        try {
            content = IOUtils.toString(gridFsResource.getInputStream(), "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return content;
    }

    private String generateHtml(String templateContent, Map model){
        //创建配置对象
        Configuration configuration = new Configuration(Configuration.getVersion());
        //模板加载器
        StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
        stringTemplateLoader.putTemplate("template", templateContent);
        configuration.setTemplateLoader(stringTemplateLoader);

        //获取模板
        String html = null;
        try {
            Template template = configuration.getTemplate("template");
            html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
        return html;

    }
}
